package com.eian.boot.common.core.utils;

import com.eian.boot.common.core.exception.SystemException;
import com.google.common.collect.Maps;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.tomcat.util.http.fileupload.ByteArrayOutputStream;
import org.springframework.beans.BeanUtils;
import org.springframework.cglib.beans.BeanCopier;
import org.springframework.cglib.core.Converter;

import java.beans.PropertyDescriptor;
import java.io.ByteArrayInputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.*;
import java.util.function.Predicate;
import java.util.stream.Collectors;

/**
 * 属性拷贝工具类
 *
 * @author alex.meng
 */
@Slf4j
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public final class BeanCopierUtils {
    private static final Map<String, BeanCopier> BEAN_COPIER_CACHE = Maps.newConcurrentMap();

    /**
     * 对象拷贝
     *
     * @param source 源对象
     * @param obj    目标对象
     */
    public static <T> void copy(Object source, T obj) {
        if (ObjectUtils.isEmpty(source)) return;

        try {
            BeanCopier copier = getBeanCopier(source.getClass(), obj.getClass(), Boolean.FALSE);
            copier.copy(source, obj, null);
        } catch (Exception e) {
            log.error("对象复制失败: source={}, targetClass={}", source, obj.getClass().getSimpleName(), e);
            throw new SystemException("对象属性复制失败", e);
        }
    }

    /**
     * 对象拷贝
     *
     * @param source 源对象
     * @param clazz  目标对象类
     * @return 目标对象 (source为null时返回null)
     */
    public static <T> T copy(Object source, Class<T> clazz) {
        return copy(source, clazz, null);
    }

    /**
     * 对象拷贝
     *
     * @param source    源对象
     * @param clazz     目标对象类
     * @param converter 自定义转换器
     * @return 目标对象 (source为null时返回null)
     */
    public static <T> T copy(Object source, Class<T> clazz, Converter converter) {
        if (ObjectUtils.isEmpty(source)) return null;

        try {
            T target = clazz.getDeclaredConstructor().newInstance();
            BeanCopier copier = getBeanCopier(source.getClass(), clazz, ObjectUtils.isNotEmpty(converter));
            copier.copy(source, target, converter);
            return target;
        } catch (Exception e) {
            log.error("对象复制失败: source={}, targetClass={}", source, clazz.getSimpleName(), e);
            throw new SystemException("对象转换复制失败", e);
        }
    }

    /**
     * 拷贝列表
     *
     * @param sourceList 源列表
     * @param clazz      目标对象类
     * @return 目标对象列表 (sourceList为null时返回空列表)
     */
    public static <T> List<T> copyList(List<?> sourceList, Class<T> clazz) {
        return copyList(sourceList, clazz, null);
    }

    /**
     * 拷贝列表
     *
     * @param sourceList 源列表
     * @param clazz      目标对象类
     * @param converter  自定义转换器
     * @return 目标对象列表 (sourceList为null时返回空列表)
     */
    public static <T> List<T> copyList(List<?> sourceList, Class<T> clazz, Converter converter) {
        if (CollectionUtils.isEmpty(sourceList)) return Collections.emptyList();

        List<T> targetList = new ArrayList<>(sourceList.size());
        try {
            Class<?> sourceClass = sourceList.getFirst().getClass();
            BeanCopier copier = getBeanCopier(sourceClass, clazz, ObjectUtils.isNotEmpty(converter));

            for (Object source : sourceList) {
                if (ObjectUtils.isEmpty(source)) continue;
                T target = clazz.getDeclaredConstructor().newInstance();
                copier.copy(source, target, converter);
                targetList.add(target);
            }
        } catch (Exception e) {
            log.error("集合(Bean)转换复制失败: sourceList={}, targetClass={}", sourceList, clazz.getSimpleName(), e);
            throw new SystemException("集合转换复制失败", e);
        }
        return targetList;
    }

    /**
     * 拷贝列表
     *
     * @param sourceList  源列表
     * @param sourceClass 数据源对象类型
     * @param targetClass 目标对象类类
     * @return 目标对象列表 (sourceList为null时返回空列表)
     */
    public static <S, T> List<T> copyList(List<S> sourceList, Class<S> sourceClass, Class<T> targetClass) {
        return copyList(sourceList, sourceClass, targetClass, null);
    }

    /**
     * 拷贝列表
     *
     * @param sourceList  源列表
     * @param sourceClass 数据源对象类型
     * @param targetClass 目标对象类类
     * @param converter   自定义转换器
     * @return 目标对象列表 (sourceList为null时返回空列表)
     */
    public static <S, T> List<T> copyList(List<S> sourceList, Class<S> sourceClass, Class<T> targetClass, Converter converter) {
        if (CollectionUtils.isEmpty(sourceList)) {
            return Collections.emptyList();
        }

        List<T> targetList = new ArrayList<>(sourceList.size());
        try {
            BeanCopier copier = getBeanCopier(sourceClass, targetClass, ObjectUtils.isNotEmpty(converter));
            for (S source : sourceList) {
                if (ObjectUtils.isEmpty(source)) continue;
                T target = targetClass.getDeclaredConstructor().newInstance();
                copier.copy(source, target, converter);
                targetList.add(target);
            }
        } catch (Exception e) {
            log.error("集合(Bean)转换复制失败: sourceList={}, sourceClass={}, targetClass={}", sourceList, sourceClass.getSimpleName(), targetClass.getSimpleName(), e);
            throw new SystemException("集合转换复制失败", e);
        }
        return targetList;
    }

    /**
     * 深拷贝对象（基于序列化）
     *
     * @param source 源对象
     * @param <T>    对象类型
     * @return 深拷贝后的新对象
     */
    @SuppressWarnings("unchecked")
    public static <T extends Serializable> T deepCopy(T source) {
        if (ObjectUtils.isEmpty(source)) return null;

        try {
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(bos);
            oos.writeObject(source);
            oos.flush();

            ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(bis);
            return (T) ois.readObject();
        } catch (Exception e) {
            log.error("深拷贝失败: {}", source.getClass().getSimpleName(), e);
            throw new SystemException("对象深拷贝失败，请确保对象实现Serializable接口", e);
        }
    }

    /**
     * 深拷贝列表（基于序列化）
     *
     * @param sourceList 源列表
     * @param <T>        对象类型
     * @return 深拷贝后的新列表
     */
    public static <T extends Serializable> List<T> deepCopyList(List<T> sourceList) {
        if (CollectionUtils.isEmpty(sourceList)) return Collections.emptyList();

        List<T> result = new ArrayList<>(sourceList.size());
        for (T item : sourceList) {
            if (item != null) {
                result.add(deepCopy(item));
            } else {
                result.add(null);
            }
        }
        return result;
    }

    /**
     * 将Map转换为Bean对象
     *
     * @param map         源Map
     * @param targetClass 目标类
     * @param <T>         目标类型
     * @return Bean对象
     */
    public static <T> T mapToBean(Map<String, Object> map, Class<T> targetClass) {
        if (MapUtils.isEmpty(map)) {
            try {
                return targetClass.getDeclaredConstructor().newInstance();
            } catch (Exception e) {
                throw new SystemException("创建目标对象实例失败", e);
            }
        }

        try {
            T target = targetClass.getDeclaredConstructor().newInstance();
            org.apache.commons.beanutils.BeanUtils.populate(target, map);
            return target;
        } catch (Exception e) {
            log.error("Map转Bean失败: map={}, targetClass={}", map, targetClass.getSimpleName(), e);
            throw new SystemException("Map转换为Bean失败", e);
        }
    }

    /**
     * 将Bean对象转换为Map
     *
     * @param bean 源Bean对象
     * @return Map对象
     */
    public static Map<String, Object> beanToMap(Object bean) {
        if (ObjectUtils.isEmpty(bean)) return Collections.emptyMap();

        Map<String, Object> map = new HashMap<>();
        Arrays.stream(BeanUtils.getPropertyDescriptors(bean.getClass()))
                .filter(pd -> pd.getReadMethod() != null)
                .forEach(pd -> {
                    try {
                        map.put(pd.getName(), pd.getReadMethod().invoke(bean));
                    } catch (Exception e) {
                        throw new RuntimeException("读取属性 " + pd.getName() + " 失败", e);
                    }
                });
        return map;
    }

    /**
     * 将Bean对象转换为Map（可过滤字段）
     *
     * @param bean   源Bean对象
     * @param filter 字段过滤器，返回true表示保留该字段
     * @return Map对象
     */
    public static Map<String, Object> beanToMap(Object bean, Predicate<String> filter) {
        if (ObjectUtils.isEmpty(bean)) return Collections.emptyMap();

        try {
            return Arrays.stream(BeanUtils.getPropertyDescriptors(bean.getClass()))
                    .filter(pd -> !"class".equals(pd.getName()))
                    .filter(pd -> pd.getReadMethod() != null)
                    .filter(pd -> filter == null || filter.test(pd.getName()))
                    .collect(Collectors.toMap(
                            PropertyDescriptor::getName,
                            pd -> {
                                try {
                                    return pd.getReadMethod().invoke(bean);
                                } catch (Exception e) {
                                    throw new RuntimeException("读取属性 " + pd.getName() + " 失败", e);
                                }
                            },
                            (existing, replacement) -> existing,
                            HashMap::new
                    ));
        } catch (Exception e) {
            log.error("Bean转Map失败: bean={}", bean, e);
            throw new SystemException("Bean转换为Map失败", e);
        }
    }

    /**
     * 带字段忽略的拷贝（浅拷贝）
     *
     * @param source       源对象
     * @param target       目标对象
     * @param ignoreFields 忽略的字段名集合
     */
    public static <T> void copyIgnoreFields(Object source, T target, Set<String> ignoreFields) {
        if (ObjectUtils.isEmpty(source) || ignoreFields == null || ignoreFields.isEmpty()) {
            copy(source, target);
            return;
        }

        try {
            Converter converter = new IgnoreFieldConverter(ignoreFields);
            BeanCopier copier = BeanCopier.create(source.getClass(), target.getClass(), true);
            copier.copy(source, target, converter);
        } catch (Exception e) {
            log.error("带忽略字段的对象复制失败: source={}, targetClass={}", source, target.getClass().getSimpleName(), e);
            throw new SystemException("对象属性复制失败", e);
        }
    }

    /**
     * 获取缓存中的BeanCopier对象
     */
    private static BeanCopier getBeanCopier(Class<?> sourceClass, Class<?> targetClass, boolean useConverter) {
        String key = sourceClass.getName() + "_" + targetClass.getName() + "_" + useConverter;
        return BEAN_COPIER_CACHE.computeIfAbsent(key,
                k -> BeanCopier.create(sourceClass, targetClass, useConverter)
        );
    }

    /**
     * 忽略字段的转换器
     */
    private record IgnoreFieldConverter(Set<String> ignoreFields) implements Converter {
        @Override
        public Object convert(Object value, Class targetClass, Object methodName) {
            if (methodName instanceof String setterName) {
                if (setterName.startsWith("set")) {
                    String fieldName = Character.toLowerCase(setterName.charAt(3)) + setterName.substring(4);
                    if (ignoreFields.contains(fieldName)) {
                        return null;
                    }
                }
            }
            return value;
        }
    }
}